![S3でVPCエンドポイント経由のアクセスのみに制限したけど特定のIAMユーザーはアクセスできるようにするバケットポリシーを作ってみた](https://devio2023-media.developers.io/wp-content/uploads/2023/08/amazon-s3.png)
S3でVPCエンドポイント経由のアクセスのみに制限したけど特定のIAMユーザーはアクセスできるようにするバケットポリシーを作ってみた
S3でVPCエンドポイントからのアクセスのみ許可するバケットポリシーを作成しましたが、マネジメントコンソールからもアクセスしたいといったことがあったので特定のIAMユーザーは許可するバケットポリシーを作成してみました。
リソース作成
バケットポリシー以外のリソースはCloudFormationで作成します。
構成図としては以下のようになります。
EC2からS3へのアクセスはVPCエンドポイント経由になるように設定を行います。
リソースの作成に使用したCloudFormationテンプレートは以下になります。
AWSTemplateFormatVersion: "2010-09-09" Description: Test Stack Parameters: # ------------------------------------------------------------# # Parameters # ------------------------------------------------------------# VPCCIDR: Default: 192.168.0.0/16 Type: String PrivateSubnet01CIDR: Default: 192.168.0.0/24 Type: String EC2VolumeSize: Default: 32 Type: Number EC2VolumeIOPS: Default: 3000 Type: Number EC2AMI: Default: ami-0310b105770df9334 Type: AWS::EC2::Image::Id EC2InstanceType: Default: t3.micro Type: String Resources: # ------------------------------------------------------------# # VPC # ------------------------------------------------------------# VPC: Type: AWS::EC2::VPC Properties: CidrBlock: !Ref VPCCIDR EnableDnsSupport: true EnableDnsHostnames: true Tags: - Key: Name Value: test-vpc # ------------------------------------------------------------# # Subnet # ------------------------------------------------------------# PrivateSubnet01: Type: AWS::EC2::Subnet Properties: AvailabilityZone: ap-northeast-1a CidrBlock: !Ref PrivateSubnet01CIDR Tags: - Key: Name Value: test-subnet-private-1a VpcId: !Ref VPC # ------------------------------------------------------------# # RouteTable # ------------------------------------------------------------# PrivateRouteTable: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref VPC Tags: - Key: Name Value: test-private-rtb PrivateRtAssociation1: Type: AWS::EC2::SubnetRouteTableAssociation Properties: RouteTableId: !Ref PrivateRouteTable SubnetId: !Ref PrivateSubnet01 # ------------------------------------------------------------# # Security Group # ------------------------------------------------------------# EC2SG: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: for EC2 GroupName: test-sg-ec2 SecurityGroupEgress: - CidrIp: 0.0.0.0/0 FromPort: -1 IpProtocol: -1 ToPort: -1 Tags: - Key: Name Value: test-sg-ec2 VpcId: !Ref VPC VPCEndpointSG: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: for VPC Endpoint GroupName: test-sg-vpc-endpoint SecurityGroupEgress: - CidrIp: 0.0.0.0/0 FromPort: -1 IpProtocol: -1 ToPort: -1 SecurityGroupIngress: - CidrIp: 192.168.0.0/16 FromPort: 443 IpProtocol: tcp ToPort: 443 Tags: - Key: Name Value: test-sg-vpc-endpoint VpcId: !Ref VPC # ------------------------------------------------------------# # IAM # ------------------------------------------------------------# EC2IAMRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Principal: Service: - ec2.amazonaws.com Action: - 'sts:AssumeRole' ManagedPolicyArns: - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore - arn:aws:iam::aws:policy/AmazonS3FullAccess RoleName: test-iam-role-ec2 Tags: - Key: Name Value: test-iam-role-ec2 EC2IAMInstanceProfile: Type: AWS::IAM::InstanceProfile Properties: InstanceProfileName: test-iam-instanceprofile-ec2 Roles: - !Ref EC2IAMRole # ------------------------------------------------------------# # EC2 # ------------------------------------------------------------# EC2: Type: AWS::EC2::Instance Properties: BlockDeviceMappings: - DeviceName: /dev/xvda Ebs: DeleteOnTermination: true Encrypted: true Iops: !Ref EC2VolumeIOPS VolumeSize: !Ref EC2VolumeSize VolumeType: gp3 DisableApiTermination: false IamInstanceProfile: !Ref EC2IAMInstanceProfile ImageId: !Ref EC2AMI InstanceType: !Ref EC2InstanceType NetworkInterfaces: - DeleteOnTermination: true DeviceIndex: 0 GroupSet: - !Ref EC2SG SubnetId: !Ref PrivateSubnet01 Tags: - Key: Name Value: test-ec2 # ------------------------------------------------------------# # VPC Endpoint # ------------------------------------------------------------# S3Endpoint: Type: AWS::EC2::VPCEndpoint Properties: RouteTableIds: - !Ref PrivateRouteTable ServiceName: !Sub com.amazonaws.${AWS::Region}.s3 VpcEndpointType: Gateway VpcId: !Ref VPC SystemsManagerEndpoint: Type: AWS::EC2::VPCEndpoint Properties: VpcEndpointType: Interface PrivateDnsEnabled: true ServiceName: !Sub com.amazonaws.${AWS::Region}.ssm VpcId: !Ref VPC SubnetIds: - !Ref PrivateSubnet01 SecurityGroupIds: - !Ref VPCEndpointSG SystemsManagerMessageEndpoint: Type: AWS::EC2::VPCEndpoint Properties: VpcEndpointType: Interface PrivateDnsEnabled: true ServiceName: !Sub com.amazonaws.${AWS::Region}.ssmmessages VpcId: !Ref VPC SubnetIds: - !Ref PrivateSubnet01 SecurityGroupIds: - !Ref VPCEndpointSG # ------------------------------------------------------------# # S3 # ------------------------------------------------------------# S3Bucket: Type: AWS::S3::Bucket Properties: BucketName: !Sub bucket-${AWS::AccountId}-${AWS::Region}
今回は検証用の為、EC2が使用するIAMロールにS3FullAccessを設定しています。
実際使用する際は必要なアクションだけに絞ったIAMポリシーを設定することをお勧めします。
デプロイは以下のコマンドで行います。
aws cloudformation create-stack --stack-name CloudFormationスタック名 --template-body file://CloudFormationテンプレートファイル名 --capabilities CAPABILITY_NAMED_IAM
デプロイが完了するとEC2にセッションマネージャーで接続することが出来ます。
接続方法は以下のドキュメントに記載されているのでご確認ください。
セッションを開始する
接続後、以下のコマンドを実行するとS3へのアクセスが確認できます。
aws s3 ls s3://S3バケット名
ここまでの手順ではまだS3にファイルを置いていないので上記のコマンドを実行しても何も表示されません。
なので、ファイルを以下のコマンドでアップロードしてみます。
cd touch test.txt aws cp ./test.txt s3://S3バケット名 aws s3 ls s3://S3バケット名
cpコマンドでS3にtest.txtをコピーしています。
lsコマンドを実行するとファイルが確認できます。
また、バケットポリシーで制限をしていないためマネジメントコンソールからも確認することが可能です。
バケットポリシーを設定する
ここから本題です。
特定の VPC エンドポイントからアクセスを許可するバケットポリシーは以下のようになります。
特定の VPC エンドポイントへのアクセスの制限
{ "Version": "2012-10-17", "Id": "Policy1415115909152", "Statement": [ { "Sid": "Access-to-specific-VPCE-only", "Principal": "*", "Action": "s3:*", "Effect": "Deny", "Resource": ["arn:aws:s3:::S3バケット名", "arn:aws:s3:::S3バケット名/*"], "Condition": { "StringNotEquals": { "aws:SourceVpce": "VPCエンドポイントID" } } } ] }
上記のバケットポリシーを設定するとマネジメントコンソールからアクセスができなくなります。
実際に設定して確認してみます。
EC2で以下のコマンドを実行してください。
policy.jsonには上記のバケットポリシーを記載してください。
aws s3api put-bucket-policy --bucket S3バケット名 --policy file://policy.json
コマンド実行後、マネジメントコンソールから該当のS3にアクセスすると以下のようになります。
EC2からのアクセスを確認してみます。
以下のlsコマンドを実行します。
aws s3 ls s3://S3バケット名
実行するとアップロードしたtest.txtが確認できます。
もし、この時点で権限エラーが発生した場合はバケットポリシーに問題があるためEC2からコマンドでバケットポリシーを更新するか、以下のドキュメントの手順に従ってルートユーザーでバケットポリシーを更新してください。
Amazon S3 バケットへのすべてのユーザーのアクセスを誤って拒否しました。アクセスを回復するにはどうしたらいいですか?
特定のIAMユーザーを許可するバケットポリシーを作成してみます。
ユーザーの制御にはaws:PrincipalArnというグローバル条件キーを使用します。
こちらの条件キーを使用することでリクエスト元のIAMユーザーのARNとバケットポリシーで設定したARNで比較を行うことが可能になります。
つまりConditionの中でaws:PrincipalArnを設定することで特定のIAMユーザーはアクセスを許可させることが可能になります。
実際に設定するバケットポリシーは以下のようになります。
{ "Version": "2012-10-17", "Id": "Policy1415115909152", "Statement": [ { "Sid": "Access-to-specific-VPCE-only", "Principal": "*", "Action": "s3:*", "Effect": "Deny", "Resource": ["arn:aws:s3:::S3バケット名", "arn:aws:s3:::S3バケット名/*"], "Condition": { "StringNotEquals": { "aws:SourceVpce": "VPCエンドポイントID", "aws:PrincipalArn": "IAMユーザーARN" } } } ] }
実際に上記のバケットポリシーを設定してみます。
設定はEC2から行います。
policy.jsonを上記のバケットポリシーに修正して以下のコマンドを実行します。
aws s3api put-bucket-policy --bucket S3バケット名 --policy file://policy.json
実行後、アクセス確認のために以下のコマンドでS3バケット内を確認します。
aws s3 ls s3://S3バケット名
EC2からは正常にtest.txtが確認できます。
次にaws:PrincipalArnで設定したIAMユーザーでマネジメントコンソールからアクセスしてみます。
ARNで指定したIAMユーザーの場合は以下のように正常にS3内のオブジェクトが確認できます。
次にaws:PrincipalArnで設定していないIAMユーザーでアクセスします。
許可されていないユーザーの場合は以下のように権限エラーが発生します。
さいごに
aws:PrincipalArnはIAMユーザー以外にもIAMロールのARNでも評価させることが可能なのでスイッチロールさせている環境にも使用することが可能です。